﻿using System.Collections.Generic;
using System.Linq;
using Expect.Core.Errors;
using Expect.Core.Interfaces;
using Expect.Core.Fixtures;

namespace Expect.Core.Helpers
{
    using System;

    /// <summary>
    /// Methods for fixture resolution as fluent methods
    /// </summary>
    static class ResolveFixtureAsFluentMethod
    {
        /// <summary>
        /// Run the row as a fluent fixture method
        /// </summary>
        /// <param name="context">execution context</param>
        /// <param name="ownerType">Owner type of method execution</param>
        /// <param name="fixture">fixture definition</param>
        /// <returns></returns>
        public static bool? AsMethodWithFixtureData(this IExecutionContext context, Type ownerType, FixtureDefinition fixture)
        {
            if (ownerType == null || fixture.ClassType != null || !string.IsNullOrEmpty(fixture.ClassName)) return null;

            var firstRow = fixture.Rows[0];
            IEnumerable<string> names = firstRow.NamesForFluent();
            IEnumerable<string> args = firstRow.ArgsForFluent();
            int numArgs = args.Count();

            string conformed = context.NameProvider.ElementName(names.ToArray());
            if (ReflectionHelper.HasMethodWithFixtureData(context, ownerType, conformed, numArgs + 1))
            {
                fixture.ClassType = typeof(MethodWithFixtureDataFixture);
                fixture.Definition = fixture.Rows[0];
                fixture.RemoveAt(0);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Run the row as a fluent fixture method
        /// </summary>
        /// <param name="context">execution context</param>
        /// <param name="owner">owner</param>
        /// <param name="fixture">fixture definition</param>
        /// <param name="definition">row definition</param>
        /// <returns></returns>
        public static bool? RunAsFluentFixtureMethod(this IExecutionContext context, object owner, FixtureDefinition fixture, FixtureRow definition)
        {
            var ownerType = owner.GetType();

            if (fixture.Rows.Count < 2)
            {
                return null;
            }

            IEnumerable<string> names = definition.NamesForFluent();
            IEnumerable<string> args = definition.ArgsForFluent();
            int numArgs = args.Count();

            string conformed = context.NameProvider.ElementName(names.ToArray());
            var method = context.GetMethodWithFixtureData(ownerType, conformed, numArgs + 1);
            if (method == null)
            {
                fixture.Status.AddError(ElementNotFoundError.Method(conformed, ownerType.FullName, numArgs + 1));
                fixture.Status.Success = null;
                return null;
            }

            context.RunMethod(method, owner, args.ToArray(), fixture.GetFixtureData());

            return true;
        }
    }
}
